#include <string>
#include <cstring>
#include <cmath>
#include <fstream>
#include <vector>
#include <algorithm>
#include "cmp.h"

using namespace std;

const int inf = 0x3f3f3f3f;

/* Kgram切片长度参数 */
const int K = 4;//3;

/* 窗口大小参数 */
const int win_size = 6;//4;

/* hash参数 */
const int p = 255;
const int mod = 13331;

/* 计算hash值 */
int cmp_hash(const char *buf);
/* 计算文件指纹 */
vector<int> cmp_get_value(const char *filename);
/* 通过指纹求相似度 */
double cmp_similar(vector<int> &f1, vector<int> &f2);

/* 比较两个文件的相似度 */
int cmp_compare(const string &file1, const string &file2)
{
    int ret;

    vector<int> value1 = cmp_get_value(file1.c_str());
    vector<int> value2 = cmp_get_value(file2.c_str());

    double tmp = cmp_similar(value1, value2);
    ret = floor(tmp * 100.0);

    return ret;
}

/* 计算hash值 */
int cmp_hash(const char *buf)
{
    int ret = 0;
    for(int i = 0; i < K; ++i)
    {
        ret = (ret * p + buf[i]) % mod;
    }
    return ret;
}

/* 计算文件指纹 */
vector<int> cmp_get_value(const char *filename)
{
    vector<int> ret;
    ifstream file;
    file.open(filename, ios::binary);
    if(!file)
    {
        /* 打开失败返回空表 */
        return ret;
    }
    file.seekg(0, ios::end);
    int file_size = file.tellg();

    int nr_gram = file_size - K + 1;
    int nr_window = nr_gram - win_size + 1;
    
    vector<int> gram;
    gram.resize(nr_gram);
    char *buf = new char[K];
    for(int i = 0; i < nr_gram; ++i)
    {
        file.seekg(i, ios::beg);
        file.read(buf, K);
        gram[i] = cmp_hash(buf);
    }
    delete[] buf;
    
    file.close();

    ret.resize(nr_window);
    for(int i = 0; i < nr_window; ++i)
    {
        int key = inf;
        for(int j = 0; j < win_size; ++j)
        {
            key = min(key, gram[i + j]);
        }
        ret[i] = key;
    }

    gram.clear();

    sort(ret.begin(), ret.end());

    return ret;
}

/* 通过指纹求相似度 */
double cmp_similar(vector<int> &f1, vector<int> &f2)
{
    /* 总数 */
    int num = f1.size() + f2.size();
    int num1 = 0, num2 = 0;

    for(auto it = f1.begin(); it != f1.end(); ++it)
    {
        if(find(f2.begin(), f2.end(), *it) != f2.end()) ++num1;
    }
    for(auto it = f2.begin(); it != f2.end(); ++it)
    {
        if(find(f1.begin(), f1.end(), *it) != f1.end()) ++num2;
    }

    /* 严格比较 static_cast<double>(max(num1, num2)) / static_cast<double>(num); */
    /* 宽松比较 static_cast<double>(min(num1, num2)) / static_cast<double>(num); */
    double ret = static_cast<double>(num1 + num2) / static_cast<double>(num);

    return ret;
}